package cc.comac.mom.util;
import lombok.NonNull;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ReflectionUtil {


    public static Object getFieldValue(@NonNull Object target, @NonNull String fieldName) throws Exception {
        Field f = getDeclaredField(target.getClass(), fieldName); // NoSuchFieldException
        f.setAccessible(true);
        return f.get(target);
    }

    /**
     * attention, won't find super class
     */
    public static List<Field> getFieldAnnotatedWithBusinessKey(Class<?> clz, String condition) {
        return getFieldAnnotatedWith(clz, BusinessKey.class, condition);
    }

    /**
     * attention, won't find super class
     */
    public static List<Field> getFieldAnnotatedWith(Class<?> clz, Class<? extends Annotation> annotation, String condition) {
        Field[] fields = clz.getDeclaredFields();
        return Arrays.stream(fields).filter(f -> {
            if (!f.isAnnotationPresent(annotation)) {
                return false;
            }
            if (condition == null) {
                return true;
            }
            List<String> conditions = new ArrayList<>();
            if (annotation == BusinessKey.class) {
                conditions = Arrays.stream(f.getAnnotation(BusinessKey.class).conditional()).collect(Collectors.toList());
            } else if (annotation == ExcelColumn.class) {
                conditions = Arrays.stream(f.getAnnotation(ExcelColumn.class).tags()).collect(Collectors.toList());
            }
            if (conditions.isEmpty() || (conditions.size() == 1 && conditions.get(0).equals(""))) {
                return true;
            }
            return conditions.contains(condition);
        }).collect(Collectors.toList());
    }

    public static Object getFieldValueSliently(@NonNull Object target, @NonNull String fieldName) {
        try {
            Field f = getDeclaredField(target.getClass(), fieldName); // NoSuchFieldException
            f.setAccessible(true);
            return f.get(target);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            //ignore them
            return null;
        }
    }

    public static void setFieldValue(@NonNull Object target, @NonNull String fieldName, Object value) throws Exception {
        Field f = getDeclaredField(target.getClass(), fieldName);
        f.setAccessible(true);
        f.set(target, value);
    }

    public static Field getDeclaredField(Class<?> clz, String fieldName) throws NoSuchFieldException {
        try {
            return clz.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            // ignore
            if (clz.getSuperclass() != null) {
                return getDeclaredField(clz.getSuperclass(), fieldName);
            } else {
                throw e;
            }
        }
    }

    public static Method getDeclaredMethod(Class<?> clz, String methodName, Class<?>... parameterTypes)
            throws NoSuchMethodException {
        try {
            return clz.getDeclaredMethod(methodName, parameterTypes);
        } catch (NoSuchMethodException e) {
            // ignore
            if (clz.getSuperclass() != null) {
                return getDeclaredMethod(clz.getSuperclass(), methodName, parameterTypes);
            } else {
                throw e;
            }
        }
    }

}
